package com.runfast.common.security.spring;

import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.Claim;
import com.runfast.common.dao.model.RunfastCuser;
import com.runfast.common.service.RunfastCuserService;
import com.runfast.common.utils.JsonUtils;
import com.runfast.common.utils.TokenUtil;
import com.runfast.common.web.entity.Result;
import com.runfast.common.web.entity.ResultCode;
import org.apache.commons.codec.Encoder;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.DispatcherServlet;
import sun.misc.CharacterEncoder;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Date;

/**
 * @author: lijin
 * @date: 2018年06月02日
 */
public class JWTAuthenticationFilter extends OncePerRequestFilter {
    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
    private RunfastCuserService cuserService;
    private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();

    private AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        String token = request.getHeader("token");

        if (StringUtils.isBlank(token)) {
            SecurityContextHolder.clearContext();
            chain.doFilter(request, response);
            return;

        }

        try {

            JWT decode = null;
            try {
                decode = JWT.decode(token);

            }catch (Exception e){
                throw new BadCredentialsException("非法的token");
            }

            Date expiresAt = decode.getExpiresAt();
            if(expiresAt!=null&&expiresAt.before(new Date())){
                throw new BadCredentialsException("token已经过期，请重新登陆");

            }
            Claim claim = decode.getClaim(TokenUtil.USER_ID);
            Integer userId = claim.asInt();

            if(userId!=null){
                RunfastCuser cuser = cuserService.selectByPrimaryKey(userId);
                if(cuser==null)throw  new BadCredentialsException("用户不存在");

                UsernamePasswordAuthenticationToken authResult = new UsernamePasswordAuthenticationToken(cuser.getMobile(),cuser.getPassword(), null);
                authResult.setDetails(this.authenticationDetailsSource.buildDetails(request));

                UserDataDetails<RunfastCuser> userDataDetails = new UserDataDetails<>(cuser.getMobile(), cuser.getPassword(), Collections.emptyList(), cuser);
                Authentication successAuthentication = createSuccessAuthentication(userDataDetails, authResult, userDataDetails);
                SecurityContextHolder.getContext().setAuthentication(successAuthentication);



            }else{
                throw new BadCredentialsException("非法的token");
            }


        }
        catch (AuthenticationException failed) {
            SecurityContextHolder.clearContext();

            onUnsuccessfulAuthentication(request, response, failed);


            return;
        }

        chain.doFilter(request, response);
    }

    protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
        /*try {
            request.getRequestDispatcher(authenticationFailUrl).forward(request,response);
        } catch (ServletException e) {
            e.printStackTrace();
        }*/

        Result fail = Result.fail(ResultCode.FAIL, failed.getLocalizedMessage());
        String respStr = JsonUtils.getMapper().writeValueAsString(fail);

        response.setContentType("application/json;charset=UTF-8;");
        PrintWriter writer = response.getWriter();
        writer.println(respStr);
        writer.flush();
    }

    public RunfastCuserService getCuserService() {
        return cuserService;
    }

    public void setCuserService(RunfastCuserService cuserService) {
        this.cuserService = cuserService;
    }


    protected Authentication createSuccessAuthentication(Object principal,
                                                         Authentication authentication, UserDetails user) {
        // Ensure we return the original credentials the user supplied,
        // so subsequent attempts are successful even with encoded passwords.
        // Also ensure we return the original getDetails(), so that future
        // authentication events after cache expiry contain the details
        MobileSmsCodeAuthenticationToken result = new MobileSmsCodeAuthenticationToken(
                principal, authentication.getCredentials(),
                authoritiesMapper.mapAuthorities(user.getAuthorities()));
        result.setDetails(authentication.getDetails());

        return result;
    }
}
